package net.sf.fc.script;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.xml.bind.JAXBException;

import org.xml.sax.SAXException;

import net.sf.fc.script.gen.settings.FileList;
import net.sf.fc.script.gen.settings.LogLevel;
import net.sf.fc.script.gen.settings.LookAndFeel;
import net.sf.fc.script.gen.settings.MostRecentlyUsed;
import net.sf.fc.script.gen.settings.Paths;
import net.sf.fc.script.gen.settings.Settings;

public class SettingsProxy {

    private final File settingsXml;
    private final Settings settings;
    private final Settings defaultSettings;
    private final ScriptHelper<Settings> settingsHelper;

    public SettingsProxy(ScriptHelper<Settings> settingsHelper, File settingsXml) throws JAXBException, IOException, SAXException {
        this.settingsHelper = settingsHelper;
        this.settingsXml = settingsXml;
        defaultSettings = createDefaultSettings();

        ensureSettingsXmlIsValid();
        settings = settingsHelper.unmarshal(settingsXml);
    }

    private void ensureSettingsXmlIsValid() throws JAXBException, IOException, SAXException {
        // If settingsXml does not exist, create a new one with the standard default settings.
        if(!settingsXml.exists()) {
            settingsHelper.marshal(settingsXml, defaultSettings);
        }

        try {
            // If validate throws an exception, create a new one with the standard default settings.
            settingsHelper.validate(settingsXml);
        } catch (IOException | SAXException e) {
            settingsHelper.marshal(settingsXml, defaultSettings);
            settingsHelper.validate(settingsXml);
        }
    }

    private Settings createDefaultSettings() {
        Settings settings = new Settings();
        MostRecentlyUsed mru = new MostRecentlyUsed();
        mru.setFileList(new FileList());
        settings.setMostRecentlyUsed(mru);
        settings.setLookAndFeel(new LookAndFeel());
        Paths paths = new Paths();
        paths.setCopyPath(System.getProperty("filecopier.scripts.copy"));
        paths.setOptionsPath(System.getProperty("filecopier.scripts.options"));
        paths.setRestorePath(System.getProperty("filecopier.scripts.restore"));
        paths.setLastSrcDirPath(System.getProperty("user.dir"));
        paths.setLastDstDirPath(System.getProperty("user.dir"));
        settings.setPaths(paths);
        return settings;
    }

    // Settings getter methods
    public Settings getSettings() {
        return (Settings)settings.copyTo(null);
    }

    public Settings getDefaultSettings() {
        return (Settings)defaultSettings.copyTo(null);
    }

    public LogLevel getLogLevel() {
        return settings.getLogLevel();
    }

    public int getMostRecentlyUsedLimit() {
        return settings.getMostRecentlyUsed().getLimit();
    }

    public FileList getMostRecentlyUsedList() {
        return (FileList)settings.getMostRecentlyUsed().getFileList().copyTo(null);
    }

    public MostRecentlyUsed getMostRecentlyUsed() {
        return (MostRecentlyUsed)settings.getMostRecentlyUsed().copyTo(null);
    }

    public String getLookAndFeelName() {
        return settings.getLookAndFeel().getSelected();
    }

    public boolean isDrawWindowBorders() {
        return settings.getLookAndFeel().isDrawWindowBorders();
    }

    public LookAndFeel getLookAndFeel() {
        return (LookAndFeel)settings.getLookAndFeel().copyTo(null);
    }

    public String getCopyPath() {
        return settings.getPaths().getCopyPath();
    }

    public String getRestorePath() {
        return settings.getPaths().getRestorePath();
    }

    public String getOptionsPath() {
        return settings.getPaths().getOptionsPath();
    }

    public String getLastSrcDirPath() {
        return settings.getPaths().getLastSrcDirPath();
    }

    public String getLastDstDirPath() {
        return settings.getPaths().getLastDstDirPath();
    }

    public Paths getPaths() {
        return (Paths)settings.getPaths().copyTo(null);
    }

    // Settings setter methods
    public void setLogLevel(LogLevel level) {
        if(level == null) throw new NullPointerException("LogLevel must not be null");
        settings.setLogLevel(level);
    }

    public void setMostRecentlyUsedLimit(int limit) {
        if(limit <= 0) throw new IllegalArgumentException("limit must be greater than 0");
        settings.getMostRecentlyUsed().setLimit(limit);
    }

    public void setMostRecentlyUsedList(FileList fileList) {
        if(fileList == null) throw new NullPointerException("FileList must not be null");
        settings.getMostRecentlyUsed().setFileList(fileList);
    }

    public void setMostRecentlyUsed(MostRecentlyUsed mru) {
        if(mru == null) throw new NullPointerException("MostRecentlyUsed must not be null");
        setMostRecentlyUsedLimit(mru.getLimit());
        setMostRecentlyUsedList(mru.getFileList());
    }

    public void setLookAndFeelName(String laf) {
        if(laf == null) throw new NullPointerException("Look and Feel name must not be null");
        settings.getLookAndFeel().setSelected(laf);
    }

    public void setDrawWindowBorders(boolean draw) {
        settings.getLookAndFeel().setDrawWindowBorders(draw);
    }

    public void setLookAndFeel(LookAndFeel laf) {
        if(laf == null) throw new NullPointerException("LookAndFeel must not be null");
        setLookAndFeelName(laf.getSelected());
        setDrawWindowBorders(laf.isDrawWindowBorders());
    }

    public void setCopyPath(String copyPath) {
        if(copyPath == null) throw new NullPointerException("Copy path string must not be null");
        settings.getPaths().setCopyPath(copyPath);
    }

    public void setRestorePath(String restorePath) {
        if(restorePath == null) throw new NullPointerException("Restore path string must not be null");
        settings.getPaths().setRestorePath(restorePath);
    }

    public void setOptionsPath(String optionsPath) {
        if(optionsPath == null) throw new NullPointerException("OptionsScript path string must not be null");
        settings.getPaths().setOptionsPath(optionsPath);
    }

    public void setLastSrcDirPath(String lastSrcDirPath) {
        if(lastSrcDirPath == null) throw new NullPointerException("Last source directory path string must not be null");
        settings.getPaths().setLastSrcDirPath(lastSrcDirPath);
    }

    public void setLastDstDirPath(String lastDstDirPath) {
        if(lastDstDirPath == null) throw new NullPointerException("Last destination directory path string must not be null");
        settings.getPaths().setLastDstDirPath(lastDstDirPath);
    }

    public void setPaths(Paths paths) {
        if(paths == null) throw new NullPointerException("Paths must not be null");
        setCopyPath(paths.getCopyPath());
        setRestorePath(paths.getRestorePath());
        setOptionsPath(paths.getOptionsPath());
        setLastSrcDirPath(paths.getLastSrcDirPath());
        setLastDstDirPath(paths.getLastDstDirPath());
    }

    public void setSettings(Settings st) {
        if(st == null) throw new NullPointerException("Settings must not be null");
        setLogLevel(st.getLogLevel());
        setLookAndFeel(st.getLookAndFeel());
        setMostRecentlyUsed(st.getMostRecentlyUsed());
        setPaths(st.getPaths());
    }

    /**
     * Put a new file at the beginning of the most recently used list.
     * @param file
     */
    public void addMostRecentlyUsedFile(File file) {
        if(file == null) throw new NullPointerException("File must not be null");
        MostRecentlyUsed mru = settings.getMostRecentlyUsed();
        List<File> files = mru.getFileList().getFiles();
        files.add(0, file);
        // See if the number of files in the list is greater than the limit. If it is greater, remove
        // the oldest one.
        int limit = mru.getLimit();
        if(files.size() > limit) {
            files.remove(limit-1);
        }
    }

    public void saveSettingsToDisk() throws JAXBException {
        Settings settingsFromDisk = settingsXml.exists() ? settingsHelper.unmarshal(settingsXml) : null;
        if(!settings.equals(settingsFromDisk)) {
            settingsHelper.marshal(settingsXml, settings);
        }
    }

}
